package cn.tedu.tmall.front.mall.filter;

import cn.tedu.tmall.front.mall.dao.repository.IUserCacheRepository;
import cn.tedu.tmall.common.pojo.po.CurrentUser;
import cn.tedu.tmall.common.pojo.po.UserStatePO;
import cn.tedu.tmall.common.util.JwtUtils;
import cn.tedu.tmall.common.web.JsonResult;
import cn.tedu.tmall.common.web.ServiceCode;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.MalformedJwtException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;

@Slf4j
@Component
public class JwtAuthorizationFilter extends OncePerRequestFilter {

    private String HEADER_AUTHORIZATION = "Authorization";

    private String secretKey = "fNesMDkqrJFdsfDSwAbFLJ8SnsHJ438AF72D73aKJSmfdsafdLKKAFKDSJ";

    @Autowired
    private IUserCacheRepository userCacheRepository;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //登录后，携带token访问内容管理系统
        log.debug("进入用户验证过滤器,门口的闸机");
        String requestURI = request.getRequestURI();
        log.debug("请求URI:{}", requestURI);
        //尝试接收客户端请求中携带的jwt数据
        String jwt = request.getHeader(HEADER_AUTHORIZATION);
        log.debug("客户端携带的jwt:{}", jwt);
        //如果未携带token，返回serviceException(401,"未登录")，前端跳转到登录页
        Claims claims;
        if (jwt == null || jwt.isEmpty()){//绕过过滤器访问Knife4j页面
            filterChain.doFilter(request, response);
            return;
        }
        try {
            claims = JwtUtils.parse(jwt, secretKey);
        } catch (MalformedJwtException e){
            log.error("jwt数据格式错误", e);
            String message = "非法访问，本次操作已被记录";
            JsonResult fail = JsonResult.fail(ServiceCode.JWT_ERROR, message);
            PrintWriter writer = response.getWriter();
            writer.println(JSON.toJSONString(fail));
            writer.close();
            return;
        }
        //解密成功，token合法，用户已登录
        log.debug("jwt解析结果:{}", claims);
        //判断用户是否有权限执行后续操作（新增文章）
        //可以通过ip地址和浏览器信息来判断你登录时候的和现在访问的是不是匹配
        log.debug("开始检查缓存中用户的状态……");
        //判断用户状态是否启用 如果禁用 返回 状态

        //续期 todo
        // name root id 1  权限列表 [增加文章,增加商品]
        //把token解析出来的那个map,从map里获取用户信息,封装成一个对象 CurrentUser
        CurrentUser currentUser = new CurrentUser(); // 当前用户
        currentUser.setId(claims.get("id", Long.class));
        currentUser.setUsername(claims.get("name",String.class));
        log.debug("当前用户:{}", currentUser);
        //token过期的处理
        Long aliveTime = claims.get("aliveTime", Long.class);
        Long now = System.currentTimeMillis();
        //if (now - aliveTime > 10000){
            //token过期，返回serviceException(401,"登录状态过期")，前端跳转到登录页
        //}
        //todo token续期

        //我们用Security 帮忙我们做权限验证
        //权限列表 [增加文章,增加商品]
        //todo 从数据库表中获取当前用户权限列表
        // 复杂的关联查询不建议用MyBatis-Plus, 还是使用mybatis自行编写sql
        // 复杂查询 每次从数据库查 有点慢 可以加缓存 redis
        UserStatePO userStatePO = userCacheRepository.getUserState(currentUser.getId());
        String authoritiesJsonString  = "";
        Integer enable = 0;
        if (userStatePO != null){
            authoritiesJsonString = userStatePO.getAuthorities();
            enable = userStatePO.getEnable();
        }
        //当管理员把用户状态设置为禁用  程序会把用户表的用户状态更新为 enable = 0 ,同时更新redis的用户状态enable值为 0 ;
        //判断用户状态
        if (enable==0){
            String message = "用户状态被禁用，请联系客服";
            JsonResult fail = JsonResult.fail(ServiceCode.USER_STATE_DISABLED, message);
            PrintWriter writer = response.getWriter();
            writer.println(JSON.toJSONString(fail));
            writer.close();
            return;
        }

//        String authoritiesJsonString  = "[{authority:'/article/add'},{authority:'/article/detail'}]"; //通过用户id获取用户权限 name root id 1  权限列表 [增加文章,增加商品]
        //把用户的权限列表 权限列表 [增加文章,增加商品] 转成 security 的 List<SimpleGrantedAuthority>
        List<SimpleGrantedAuthority> authorities
                = JSON.parseArray(authoritiesJsonString, SimpleGrantedAuthority.class);
        //构建 security Authentication对象 入参 1 当前用户, 2 不用管 3 权限列表
        Authentication authentication = new UsernamePasswordAuthenticationToken(
                currentUser, null, authorities);

        // 将security Authentication对象存入到SecurityContext中 上下文
        SecurityContext context = SecurityContextHolder.getContext();
        context.setAuthentication(authentication);

        //可以理解为放到安全框架map里
        /*
         *  Security
         *  map put
         *      1,[增加商品,增长文章]
         *      2,[文章列表]
         *  map.get(1) ==  [增加商品,增长文章]
         *  [增加商品,增长文章] 是否包含 增加商品 是 放行 不是 没权限
         */
        //过滤器链继续执行，即放行
        log.debug("验证JWT通过，向SecurityContext存入认证信息，放行");
        filterChain.doFilter(request, response);
    }
}
